
import { defineComponent, defineAsyncComponent, resolveComponent, toRaw, nextTick } from 'vue';


let BitmapFonts = defineAsyncComponent(async () => {
    if (!BitmapFonts.hanzi_by_frequency) {
        BitmapFonts.hanzi_by_frequency = (await axios('./components/bitmapFonts/hanzi_by_frequency.json')).data;
    }
    let webCustomFonts = function () {
        var fontFaces = [];
        document.fonts.forEach(font => {
            fontFaces.push(font.family);
        })
        return fontFaces;
    }
    return {
        template: (await axios('./components/bitmapFonts/bitmapFonts.html')).data,
        components: {},
        data() {
            return {
                currentFont: null,
                avaliableFonts: [...webCustomFonts(),
                    'serif', 'monospace', 'sans-serif', 'cursive', 'fantasy', 'script',
                    '等线', '黑体', '宋体', '楷体', '仿宋', '隶书', '幼圆', '华文细黑', '微软雅黑'],
                customFonts: [],
                customChatStr: '',
                colors: [{ r: 0, g: 0, b: 0 }, { r: 255, g: 255, b: 255 }],
                displayChar: '',
                options: null,
                enabled: false,
            }
        },
        watch: {
            customChatStr(v) {
                if (this.currentFont) {
                    this.currentFont.customChars = [...new Set(Array.from(v))].sort();
                }
            },
            currentFont(v) {
                this.customChatStr = v.customChars.join('');
            },
            displayChar() {
                this.redraw();
            },
            'currentFont.font'() {
                this.redraw();
            },
            'currentFont.size'() {
                this.redraw();
            },
            'currentFont.luminance'() {
                this.redraw();
            },
            'currentFont.stroke'() {
                this.redraw();
            },
            enabled(enabled) {
                if (!enabled) {
                    this.save();
                    OB_IDE.removeComponent(this);
                } else {
                    this.load();
                }
            }
        },
        methods: {
            save() {
                let conf = { fonts: toRaw(this.customFonts) };
                VFS.partition.config.put("bitmapFonts.json", conf);
            },
            load() {

                if (VFS.partition.config) {
                    VFS.partition.config.get("bitmapFonts.json", (conf) => {
                        if (!conf) {
                            conf = { fonts: [BitmapFonts.defaultFont()] };
                        }
                        if (conf.fonts.length == 0) {
                            conf.fonts.push(BitmapFonts.defaultFont())
                        }
                        this.customFonts = conf.fonts;
                        this.currentFont = this.customFonts[0];
                        this.customChatStr = this.currentFont.customChars.join('');
                    });
                }
            },
            reload() {
                let t = this.customFonts;
                this.customFonts = null;
                this.customFonts = t;
                t = this.currentFont;
                this.currentFont = null;
                this.currentFont = t;
            },
            redraw() {
                let canvas = this.$refs.bitmapFontsCanvas;
                if (!canvas) {
                    nextTick(this.redraw);
                    return;
                }
                if (this.currentFont) {
                    BitmapFonts.drowOnCanvas(canvas, this.currentFont, this.displayChar, this.options.colors || this.colors);
                }
            },
            showSelectCustomChar() {
                let selectedText = window.getSelection().toString();
                if (selectedText) {
                    this.displayChar = Array.from(selectedText)[0];
                }
            },
            async addFonts() {
                let name = await OB_IDE.asyncPrompt(OB_IDE.$t('请输入字体名称'), 'font');
                if (name) {
                    let o = this.customFonts.find(f => f.name == name);
                    if (!o) {
                        let f = JSON.parse(JSON.stringify(
                            this.currentFont ?
                                toRaw(this.currentFont)
                                : BitmapFonts.defaultFont()));
                        f.id = Math.random().toString(36).substring(2);
                        f.name = name;
                        f.additionalChinese = 'none';
                        this.customFonts.push(f);
                        this.currentFont = f;
                    } else {
                        this.currentFont = o;
                    }
                }
            },
            async rename() {
                if (this.currentFont) {
                    this.currentFont.name = await OB_IDE.asyncPrompt(OpenBlock.i("请输入新名字"), this.currentFont.name);
                    this.reload();
                }
            },
            removeFont() {
                let p = this.customFonts.findIndex(f => f.name == this.currentFont.name);
                if (p >= 0) {
                    this.customFonts.splice(p, 1);
                    this.currentFont = this.customFonts[0] || BitmapFonts.defaultFont();
                }
            }
        }
    }
});
// 创建一个新的style元素  
var style = document.createElement('style');

// 添加样式内容  
style.innerHTML = `
.OB-bitmapFonts-window .ivu-modal-header{
    display: none;
}
#bitmapFontsCanvas{
    width:50%;
    height:auto;
    image-rendering: pixelated;
    border:1px solid gray;
    float:left;
    margin-right:10px;
}
#OB-bitmapFonts-settings{
    float:left;
    width:calc(50% - 10px);
}
#OB-bitmapFonts-chars{
    float:left;
    width:100%;
    max-height:300px;
    overflow-y:auto;
    margin-top:10px;
}
`;

// 将style元素添加到head中  
document.head.appendChild(style);
BitmapFonts.openSettings = async function (options) {
    await OpenBlock.saveAllSrc();
    let win = await OB_IDE.addComponent(BitmapFonts);
    win.options = options;
    win.enabled = true;
};
function groupArray(arr, size) {
    var result = [];
    for (var i = 0; i < arr.length; i += size) {
        result.push(arr.slice(i, i + size));
    }
    return result;
}
BitmapFonts.defaultFont = function () {
    return {
        size: 15, font: 'serif', luminance: 240, name: 'serif', id: Math.random().toString(36).substring(2),
        customChars: [], stroke: false,
        "additionalChinese": "none"
    };
}
BitmapFonts.drowOnCanvas = function (canvas, bitmapFont, displayChar, colors) {
    if (!colors) {
        colors = [{ r: 0, g: 0, b: 0 }, { r: 0, g: 0, b: 255 }]
    }
    let ctx = canvas.getContext('2d', { willReadFrequently: true });
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.textBaseline = 'middle';
    ctx.font = (bitmapFont.size) + 'px  ' + bitmapFont.font;
    if (bitmapFont.stroke) {
        ctx.strokeText(displayChar, 1, bitmapFont.size / 2, bitmapFont.size);
    } else {
        ctx.fillText(displayChar, 1, bitmapFont.size / 2, bitmapFont.size);
    }

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
    let points = groupArray(data, 4).map(g => g.reduce((t, v) => t + v, 0));
    points.forEach((v, i, points) => {
        let color;
        if (v < 255 - bitmapFont.luminance) {
            color = colors[0];
        } else {
            color = colors[1];
        }
        let style = '#' + color.r.toString(16).padStart(2, '0') + color.g.toString(16).padStart(2, '0') + color.b.toString(16).padStart(2, '0');
        ctx.fillStyle = style;
        ctx.fillRect(i % canvas.width, Math.floor(i / canvas.width), 1, 1);
    });
};
BitmapFonts.charToBytes = function (c, font) {
    const canvas = new OffscreenCanvas(font.size, font.size);
    BitmapFonts.drowOnCanvas(canvas, font, c, null);
    const offscreenCtx = canvas.getContext('2d', { willReadFrequently: true });
    const imageData = offscreenCtx.getImageData(0, 0, font.size, font.size);
    const data = imageData.data;
    let points = groupArray(data, 4).map(g => g[0] + g[1] + g[2] > 0);
    let bytePoints = groupArray(points, 8).map(g =>
        //(g[15]) << 15 | (g[14]) << 14 | (g[13]) << 13 | (g[12]) << 12 | (g[11]) << 11 | (g[10]) << 10 | (g[9]) << 9 | (g[8] << 8) |
        (g[7]) << 7 | (g[6]) << 6 | (g[5]) << 5 | (g[4]) << 4 | (g[3]) << 3 | (g[2]) << 2 | (g[1]) << 1 | (g[0]));
    return bytePoints;
};
BitmapFonts.char3 = function (i) {
    function toHex(m) {
        return '0x' + ((i >> m) & 0xFF).toString(16);
    }
    return `${toHex(16)},${toHex(8)},${toHex(0)}`;
}
BitmapFonts.toC = async function (scene) {
    if (!BitmapFonts.hanzi_by_frequency) {
        BitmapFonts.hanzi_by_frequency = (await axios('./components/bitmapFonts/hanzi_by_frequency.json')).data;
    }
    let config = await VFS.partition.config.get("bitmapFonts.json");
    let fonts = config?.fonts;
    if (!fonts) {
        return '/* no bitmap fonts */';
    }
    let srcs = OpenBlock.BlocklyParser.loadedFiles.srcs;
    srcs = srcs.filter(s => scene.srcList.indexOf(s.name) >= 0);
    let requiredString = srcs.map(s => s.__compiled ? Object.keys(s.__compiled.relocation.string).reduce((a, b) => a + b).split('') : []).flat();
    let requiredChat = [...new Set(requiredString.flat()),
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    let code = [];
    let IdNamePairs = [];
    const encoder = new TextEncoder();
    fonts.forEach((font, i) => {
        let cChars = [];
        let cCharIndex = [];
        // 去除空格，排序后，重新添加到最前
        let allChars = requiredChat.concat(Array.from(font.customChars)).filter(c => c !== ' ');
        allChars = [...new Set(allChars.flat())];
        switch (font.additionalChinese) {
            case '1000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 1000));
                break;
            case '2000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 2000));
                break;
            case '3000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 3000));
                break;
            case '4000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 4000));
                break;
            case '5000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 5000));
                break;
            case '6000':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency.splice(0, 6000));
                break;
            case 'all':
                allChars = allChars.concat(BitmapFonts.hanzi_by_frequency);
                break;
        }
        allChars.sort();
        allChars.unshift(' ');
        let bytesPerChar = 0;
        let indexLen = 0;
        allChars.map((c, i1) => {
            let b = BitmapFonts.charToBytes(c, font);
            bytesPerChar = b.length;
            // 点阵字体数组
            cChars.push(`/* ${c} */ ` + b.map(v => '0x' + v.toString(16).padStart(4, '0')).join(',') + ',');
            // 调用encode方法将字符串编码为UTF-8格式的Uint8Array
            const utf8EncodedArray = encoder.encode(c);

            // 如果需要将Uint8Array进一步转换为纯数字数组（每个元素代表一个字节）
            const utf8NumbersArray = Array.from(utf8EncodedArray);
            // 点阵索引数组
            // 因为 utf-8 首字节不会出现 0xff，既不会出现 0x00 0xFF 的连续组合。
            // 每一个文字最后用0x00结束，所以索引首字节可用0xFF分割
            // 索引末字节可用0x00分割

            cCharIndex.push('0xFF,' + BitmapFonts.char3(i1) + ` /* ${c} */ ` + ',0,' + utf8NumbersArray.map((v, k) => { return '0x' + v.toString(16) }).join(',') + ',0');
            indexLen += 1 + 3 + 1 + utf8NumbersArray.length + 1;
        });
        code.push(
            `\nstatic const unsigned char cChars_${font.id}[] = {\n` + cChars.join('\n') + `\n};\n` +
            `static const unsigned char cCharIndex_${font.id}[] = {\n` + cCharIndex.join(',\n') + '\n};\n' +
            `BitmapFontDef Font_${font.id} = {${font.size},${font.size},(char*)cChars_${font.id},${bytesPerChar},cCharIndex_${font.id},${indexLen},${cChars.length}};\n`);
        IdNamePairs.push({ id: font.id, name: font.name });
    });
    let c = code.join('\n');
    let idx = IdNamePairs.map((v, i) => {
        return `
    if(strcmp(name, "${v.name}") == 0){
        return Font_${v.id};
    }
    `;
    }).join('\n');
    let search = `
static BitmapFontDef getBitmapFont(char* name){
    ${idx}
    printf("Unknown font %s\\n",name);
    abort();
}
`;
    return c + search;
};
// ${[...new Set(fonts.map((f, i) => { return `uint8_t bitmapFont${i}[${"3" || Math.ceil(f.size * f.size / 8)}];`; }))].join('\n')}
export default BitmapFonts;
BitmapFonts.install = function () {
    class BitmapFontProvider extends OB_NativeBlockArgumentProvider {
        /**
         * 下拉列表的选项
         * @type {Array.<Object.<string,String>>|Function} 国际化->返回值的键值对
         */
        options;
        /**
         * @type {String}
         */
        arg;
        /**
         * @type {String}
         */
        parentBlockType;
        workspace;
        /**
         * 
         * @param {xmldom} blockDom 本地块xml配置中的block节点
         * @param {xmldom} providerCfg 本地块配置中的provider配置
         */
        init(fieldName, blockDom, providerCfg) {
            this.arg = fieldName;
            this.parentBlockType = blockDom.getAttribute('nativeCall');
            this.options = this._options.bind(this);
        }
        /**
         * 
         * @param {Blockly.Workspace} workspace
         * @param {String} checkType 需要的类型
         * @param {String} inputName input名称
         * @param {Blockly.Input} input input对象
         */
        makeBlock(workspace, checkType, inputName, input) {
            if (checkType && checkType !== 'String') {
                throw Error('BitmapFontProvider 只支持 String 类型');
            }
            this.workspace = workspace;
            let blk = workspace.newBlock('empty_provider');
            blk.mutation = JSON.stringify({ parentType: this.parentBlockType, argName: this.arg, checkType: 'String' });
            blk.updateBlock();
            return blk;
        }
        _options() {
            let ret = [];
            if (VFS.partition.config) {
                VFS.partition.config.get("bitmapFonts.json", (conf) => {
                    if (!conf) {
                        return;
                    }
                    conf.fonts.forEach(f => {
                        ret.push([f.name, f.name]);
                    });
                });
            }
            if (ret.length == 0) {
                ret.push(['', '']);
            }
            return ret;
        }
    }
    OpenBlock.registerFieldProvider('BitmapFontProvider', BitmapFontProvider);
};